package cn.edu.thu.laud.thrift;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;

import cn.edu.thu.laud.com.utils.IOUtils;
import cn.edu.thu.laud.jdbc.util.LaUDJdbcUtils;
import cn.edu.thu.laud.server.thrift.FileInfo;


public class LaUDDataClient {
	LaUDClient client;
	Socket dataSocket;
	final String dataAddr;
	final int dataPort;
	private DataInputStream inData=null;
	private DataOutputStream outData=null;

	public LaUDDataClient(String dataAddr,int dataPort,LaUDClient client) {
		super();
		this.client = client;
		this.dataAddr=dataAddr;
		this.dataPort=dataPort;
	}


	public void closeDataConnect(){
		IOUtils.close(dataSocket);
	}
	public void sendClose(){
		try {
			this.init(new FileInfo().setCheckcode(""));
			boolean close=inData.readBoolean();
			if(!close){
				System.err.println("send close command failed.");
			}else{
				System.out.println("send close command successful");
			}
		} catch (Exception e) {
			//e.printStackTrace();
		}finally{
			this.closeDataConnect();
		}
	}

	private void init(FileInfo fileInfo) throws Exception{
		if(dataSocket==null||dataSocket.isClosed()||!dataSocket.isConnected()){
			try {
				dataSocket=new Socket(InetAddress.getByName(this.dataAddr),this.dataPort);
			} catch (Exception e) {
				e.printStackTrace();
				dataSocket=null;
				throw e;
			} 
		}
		try {
			//if(inData==null)
				inData = new DataInputStream( dataSocket.getInputStream());
		//	if(outData==null)
				outData = new DataOutputStream( dataSocket.getOutputStream());
		} catch (IOException e) {
			e.printStackTrace();
			closeDataConnect();
			throw e;
		}
		try {
			outData.writeUTF(client.getSessionId());
			outData.writeUTF(fileInfo.checkcode);
		} catch (IOException e) {
			e.printStackTrace();
			closeDataConnect();
			throw new IOException("wrong user.");
		}
		if(dataSocket.isClosed()){
			throw new IOException("connect failed.");
		}
	}
	
	
	/**
	 * 
	 * upload or download files
	 * @param result
	 * @throws Exception 
	 */
	public byte[] fileTransfer(FileInfo fileInfo,char direction) throws Exception {

		this.init(fileInfo);
		//boolean isok=false;
		byte[] file=null;
		switch (direction) {
		case LaUDJdbcUtils.WRITE_INTO:
			this.upload(fileInfo.info);
			inData.readBoolean();//read isSaved				
			break;
		case LaUDJdbcUtils.READ_FROM:
			file=this.download(fileInfo);
			break;
		default:
			outData.writeChar(LaUDJdbcUtils.OTHERS);
			break;
		}
		inData.readBoolean();//receive "go on" command.
		return file;
	}
	private byte[] download(FileInfo fileInfo) throws IOException {
		outData.writeChar(LaUDJdbcUtils.READ_FROM);
		long size=-1;
		String filePath;
		String localpath=fileInfo.getInfo();
		byte[] file=null;
		if(localpath==null){
			localpath="./";
		}
		boolean isFolder;
		isFolder = inData.readBoolean();
		File tempfile=new File(localpath);

		
		
		if(isFolder&&tempfile.exists()&&tempfile.isFile()){
			//error . 
			IOUtils.close(inData);
			closeDataConnect();
			throw new IOException("local disk has existed a file,but we want to write to local disk a folder.");
		}
		else if(isFolder ||tempfile.isDirectory()){
			localpath=tempfile.getAbsolutePath().replace('\\', '/')+"/";

			while((size=inData.readLong())!=-1){
				System.out.println("fileSize:"+size);
				filePath=localpath+inData.readUTF();
				File newfile=new File(filePath);
				System.out.println("filePath:"+filePath);
				if(!newfile.getParentFile().exists())
					newfile.getParentFile().mkdirs();
				FileOutputStream fout=new FileOutputStream(newfile);
				try{
					IOUtils.copyStream(inData, fout, 4096, size, null);
				}finally{
					IOUtils.close(fout);
				}
			}

		}else{
			while((size=inData.readLong())!=-1){
				inData.readUTF();
				filePath=tempfile.getAbsolutePath();
				File newfile=tempfile;
				System.out.println("filePath:"+filePath);
				newfile.getParentFile().mkdirs();
				FileOutputStream fout=new FileOutputStream(newfile);
				try{
					IOUtils.copyStream(inData, fout, 4096, size, null);
				}finally{
					fout.close();
				}
			}
		}
		return file;
	}

	private boolean upload(String info) throws IOException {
		File file=new File(info);	
		outData.writeChar(LaUDJdbcUtils.WRITE_INTO);
		if(file.isDirectory()){
			readFolder(file,outData,(File.separatorChar=='\\'),info.length());
		}
		else{
			outData.writeLong(file.length());
			outData.writeUTF(file.getName());
			FileInputStream fin;
			fin = new FileInputStream(file);
			IOUtils.copyStream(fin, outData, 4096, file.length(), null);
		}
		outData.writeLong(-1);	
		return true;
	}

	private void readFolder(File file, DataOutputStream outData,boolean replaced, int replacementPoint) throws IOException {
		File[] files=file.listFiles();
		for (File f:files) {
			if (f.isFile()) {
				outData.writeLong(f.length());
				if(replaced){
					System.out.println("file(replaced):"+file.getAbsolutePath().replaceAll("\\\\", "/"));
					outData.writeUTF(f.getAbsolutePath().replaceAll("\\\\", "/").substring(replacementPoint));
				}else{
					System.out.println("file:"+file.getAbsolutePath().replaceAll("\\\\", "/"));
					outData.writeUTF(f.getAbsolutePath().substring(replacementPoint));
				}
				FileInputStream  fin=new FileInputStream(f);
				IOUtils.copyStream(fin, outData, 4096, f.length(), null);
			} else if (f.isDirectory()) {
				readFolder(f,outData,replaced,replacementPoint);
			}
		}
	}
	public boolean isClosed(){
		return dataSocket.isClosed();
	}
//	public static enum FileDirection{
//		UPLOAD,DOWNLOAD
//	}
}
